import { uuid } from '@jsplumb/browser-ui'

export interface DataFlowNode {
  id: string
  name: string
  type: string
  info: any
}

export interface ClipboardNode {
  id: string
  name: string
  type: string
  info: any
  position: {
    left: number
    top: number
  }
}

export class DataFlow {
  id: string = ''
  name: string = ''
  nodes: Map<string, DataFlowNode> = new Map()
  //<source,<targets>>
  connectors: Map<string, Set<string>> = new Map()
  selectedNodes: Set<string> = new Set<string>()
  clipboard: {
    position: {
      left: number
      top: number
    }
    nodes: Set<ClipboardNode>
  } = {
    position: {
      left: 0,
      top: 0
    },
    nodes: new Set<ClipboardNode>()
  }

  init(name: string): string {
    this.name = name
    this.id = uuid()
    return this.id
  }

  clearClipboard() {
    this.clipboard = {
      position: {
        left: 0,
        top: 0
      },
      nodes: new Set<ClipboardNode>()
    }
  }

  addNode(id: string, name: string, type: string): DataFlowNode {
    const node: DataFlowNode = {
      id: id,
      name: name,
      type: type,
      info: {}
    }
    this.nodes.set(node.id, node)
    return node
  }

  addConnector(from: string, to: string): boolean {
    if (this.connectors.get(from)?.has(to)) {
      return false
    }
    //检测是否存在环
    if (this.getDownstreamNodeIds(to).has(from)) {
      return false
    }
    const set = this.connectors.get(from) || new Set<string>()
    set.add(to)
    this.connectors.set(from, set)
    return true
  }

  deleteConnector(from: string, to: string) {
    const set = this.connectors.get(from)
    set?.delete(to)
    if (!set || set.size === 0) {
      this.connectors.delete(from)
    }
  }

  getDownstreamNodeIds(id: string) {
    const downstream = new Set<string>()
    const nextIds = this.connectors.get(id)
    if (nextIds) {
      for (const next of nextIds) {
        downstream.add(next)
        this.getDownstreamNodeIds(next).forEach((id) => downstream.add(id))
      }
    }
    return downstream
  }

  selectOneNode(id: string) {
    if (this.selectedNodes.has(id)) {
      return
    } else {
      this.selectedNodes.clear()
      this.selectedNodes.add(id)
    }
  }

  selectIncrementNode(id: string) {
    if (this.selectedNodes.has(id)) {
      this.selectedNodes.delete(id)
    } else {
      this.selectedNodes.add(id)
    }
  }

  setSelectedNode(ids: string[]) {
    this.selectedNodes.clear()
    ids.forEach((id) => this.selectedNodes.add(id))
  }

  selectAllNode() {
    const ids = [...this.nodes.keys()]
    ids.forEach((id) => this.selectedNodes.add(id))
  }

  clearSelectedNode() {
    this.selectedNodes.clear()
  }
}
